﻿using Sgr.Domain.Entities;
using Sgr.Domain.Managers;
using Sgr.Exceptions;
using Sgr.Generator;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Sgr.UPMS.Domain.Menus
{
    /// <summary>
    /// 菜单管理实现类
    /// </summary>
    public class MenuManage : TreeNodeManageBase<Menu, long>, IMenuManage
    {
        private readonly ISegmentNumIdGenerator _numberIdGenerator;

        // 添加一个属性来安全地转换基类的仓储引用
        private IMenuRepository _menuRepository =>
            (IMenuRepository)_repository;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="repository">行政区划仓储</param>
        /// <param name="numberIdGenerator">ID生成器</param>
        public MenuManage(
            IMenuRepository repository,
            ISegmentNumIdGenerator numberIdGenerator) : base(repository)
        {
            _numberIdGenerator = numberIdGenerator ?? throw new ArgumentNullException(nameof(numberIdGenerator));
        }

        /// <summary>
        /// 创建菜单
        /// <para>规则1：同级菜单名称唯一性</para>
        /// <para>规则2：菜单层级不超过5级</para>
        /// <para>规则3：目录节点只能放置到目录节点下</para>
        /// <para>规则4：菜单节点只能放置到目录节点下</para>
        /// </summary>
        /// <param name="name">菜单名称</param>
        /// <param name="type">菜单类型</param>
        /// <param name="parentId">父级菜单ID</param>
        /// <param name="permission">权限标识</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>创建的菜单</returns>
        public async Task<Menu> CreateAsync(
            string name,
            MenuType type,
            long? parentId = null,
            string? permission = null,
            CancellationToken cancellationToken = default)
        {
            // 处理父节点ID及节点路径
            parentId ??= 0;

            // 验证同级菜单名称唯一性
            DomainRuleCheckResult ruleCheckResult = await ValidateNameUniquenessAsync(name, parentId.Value, null, cancellationToken);
            if (!ruleCheckResult.IsComply)
                throw DomainException.ValidationError(ruleCheckResult.Message);

            // 获取ID及节点路径
            long id = await _numberIdGenerator.GetUniqueIdAsync(UpmsConstant.DEFAULT_SEGMENT_KEY, cancellationToken);

            // 如果有父节点，验证父节点存在且级别合适
            Menu? parentNode = null;
            if (parentId > 0)
            {
                parentNode = await _menuRepository.GetAsync(parentId.Value, cancellationToken);
                if (parentNode == null)
                    throw DomainException.ValidationError($"上级菜单ID {parentId} 不存在");

                // 验证菜单类型是否兼容
                var menuTypeCompatibility = ValidateMenuTypeCompatibility(parentNode, type);
                menuTypeCompatibility.HandleRule();
            }

            // 生成节点路径
            string nodePath = GetNodePath(parentNode, id, 5);

            // 创建菜单实例
            var entity = new Menu(name, type, id.ToString(), permission)
            {
                Id = id
            };
            entity.SetParent(parentId.Value, nodePath);
            return entity;
        }

        /// <summary>
        /// 验证同级菜单名称是否唯一
        /// </summary>
        /// <param name="name">菜单名称</param>
        /// <param name="parentId">父级菜单ID</param>
        /// <param name="excludeId">需要排除的菜单ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <exception cref="DomainException">当名称重复时抛出异常</exception>
        public async Task<DomainRuleCheckResult> ValidateNameUniquenessAsync(string name,
                 long parentId,
                 long? excludeId = null,
                 CancellationToken cancellationToken = default)
        {
            if (await _menuRepository.IsNameExistsInSameLevelAsync(name, parentId, excludeId, cancellationToken))
                return DomainRuleCheckResult.Fail($"同级菜单下已存在名称为 {name} 的菜单");

            return DomainRuleCheckResult.Ok();
        }

        /// <summary>
        /// 验证路由名称是否唯一
        /// </summary>
        /// <param name="routeName"></param>
        /// <param name="excludeId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<DomainRuleCheckResult> ValidateRouteNameUniquenessAsync(string routeName,
            long? excludeId = null,
            CancellationToken cancellationToken = default)
        {
            if (await _menuRepository.IsRouteNameExistsAsync(routeName, excludeId, cancellationToken))
                return DomainRuleCheckResult.Fail($"已存在路由名称为 {routeName} 的菜单");

            return DomainRuleCheckResult.Ok();
        }

        /// <summary>
        /// 激活菜单及其子菜单
        /// </summary>
        /// <param name="menu">菜单</param>
        /// <param name="cancellationToken">取消令牌</param>
        public async Task ActivateAsync(Menu menu, CancellationToken cancellationToken = default)
        {
            menu.Activate();
            await _menuRepository.UpdateAsync(menu, cancellationToken);

            // 获取子菜单并递归激活
            var children = await _menuRepository.GetChildNodesRecursionAsync(menu.NodePath, cancellationToken);
            foreach (var child in children)
            {
                child.RestoreIfDeactivatedByParent();
                await _menuRepository.UpdateAsync(child, cancellationToken);
            }
        }

        /// <summary>
        /// 停用菜单及其子菜单
        /// </summary>
        /// <param name="menu">菜单</param>
        /// <param name="cancellationToken">取消令牌</param>
        public async Task DeactivateAsync(Menu menu, CancellationToken cancellationToken = default)
        {
            menu.Deactivate();
            await _menuRepository.UpdateAsync(menu, cancellationToken);

            // 获取子菜单并递归停用
            var children = await _menuRepository.GetChildNodesRecursionAsync(menu.NodePath, cancellationToken);
            foreach (var child in children)
            {
                child.Deactivate(true);
                await _menuRepository.UpdateAsync(child, cancellationToken);
            }
        }

        /// <summary>
        /// 重写基类中验证移动操作是否有效的验证规则
        /// </summary>
        /// <param name="sourceNode">源节点</param>
        /// <param name="targetId">目标节点ID</param>
        /// <param name="maxLevel">最大层级限制</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>验证结果</returns>
        public override async Task<DomainRuleCheckResult> ValidateMoveOperationAsync(
            Menu sourceNode,
            long targetId,
            int maxLevel = 10,
            CancellationToken cancellationToken = default)
        {
            // 如果目标ID是默认值，表示移动到根节点，这是合法的
            if (targetId!.Equals(0))
                return DomainRuleCheckResult.Ok();

            // 检查目标节点是否存在
            var targetNode = await _repository.GetAsync(targetId, cancellationToken);
            if (targetNode == null)
                return DomainRuleCheckResult.Fail($"目标节点(ID:{targetId})不存在");

            // 源节点不能是目标节点的父节点或祖先节点
            if (targetNode.NodePath.Contains($"{sourceNode.Id}"))
                return DomainRuleCheckResult.Fail("不能将节点移动到其子节点或后代节点下");

            // 校验目标节点层级是否已达到最大限制
            if (targetNode.NodePath.Split('#').Length >= maxLevel)
                return DomainRuleCheckResult.Fail($"目标节点已达到最大层级限制({maxLevel})，无法移动");

            // 新增逻辑 ********************************************************************

            //如果源节点是否目录，那么目标节点也必须是目录
            return ValidateMenuTypeCompatibility(targetNode, sourceNode.Type);
        }

        private static DomainRuleCheckResult ValidateMenuTypeCompatibility(Menu targetNode, MenuType sourceNodeMenuType)
        {
            if (sourceNodeMenuType == MenuType.Directory && targetNode.Type != MenuType.Directory)
                return DomainRuleCheckResult.Fail("目录节点只能放置到目录节点下");

            //如果源节点是菜单，那么目标节点必须是目录
            if (sourceNodeMenuType == MenuType.Menu && targetNode.Type != MenuType.Directory)
                return DomainRuleCheckResult.Fail("菜单节点只能放置到目录节点下");

            return DomainRuleCheckResult.Ok();
        }

        /// <summary>
        /// 删除某个节点前的操作
        /// </summary>
        /// <param name="entity"></param>
        protected override void BeforeDeleteEntity(Menu entity)
        {
            base.BeforeDeleteEntity(entity);

            entity.Deactivate();
        }

        /// <summary>
        /// 修改树节点的父节点
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="parentEntity"></param>
        /// <param name="maxLevel"></param>
        protected override void ChangeEntityParent(Menu entity, Menu? parentEntity, int maxLevel = 5)
        {
            long parentId = 0;

            if (parentEntity != null)
            {
                parentId = parentEntity!.Id;
            }

            string path = GetNodePath(parentEntity, entity.Id, maxLevel);

            entity.SetParent(parentId, path);
        }
    }
}